package com.gcxy.service.impl;


import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.gcxy.core.PageVO;
import com.gcxy.core.R;
import com.gcxy.dto.semester.SemesterInfoDTO;
import com.gcxy.dto.semester.SemesterPageDTO;
import com.gcxy.entity.Semester;
import com.gcxy.mapper.SemesterMapper;
import com.gcxy.service.SemesterService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.gcxy.utils.BeanCopyUtil;
import com.gcxy.utils.UserUtil;
import com.gcxy.vo.semester.SemesterInfoVO;
import com.gcxy.vo.semester.SemesterPageVO;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;

import javax.annotation.Resource;
import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;


/**
 * <p>
 * 学期信息 服务实现类
 * </p>
 *
 * @author Huang先森
 * @since 2024-07-22
 */
@Service
public class SemesterServiceImpl extends ServiceImpl<SemesterMapper, Semester> implements SemesterService {

    @Resource
    private SemesterMapper semesterMapper;

    /**
     * 新增学期信息
     *
     * @param semesterInfoDTO
     * @return
     */
    @Override
    public R<?> insertSemester(SemesterInfoDTO semesterInfoDTO) {

        // 校验学期时间间隔不超过7个月
        if (!isWithinSevenMonths(semesterInfoDTO.getStartTime(), semesterInfoDTO.getEndTime())) {
            return R.Failed("学期时间间隔不能超过7个月");
        }

        // 校验名称是否合法
        if (!isValidSemesterName(semesterInfoDTO.getName())) {
            return R.Failed("学期名称格式错误，学期格式只能为：例2023-2024学年第二学期");
        }

        // 校验名称是否存在
        LambdaQueryWrapper<Semester> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(Semester::getName, semesterInfoDTO.getName());
        Semester semester2 = getOne(queryWrapper);
        if (!ObjectUtils.isEmpty(semester2)) {
            return R.Failed("学期名称重复！该学期已经存在！");
        }

        // 校验学期时间是否重叠
        queryWrapper.clear();
        queryWrapper.ge(Semester::getEndTime, semesterInfoDTO.getStartTime())
                .le(Semester::getStartTime, semesterInfoDTO.getEndTime());
        List<Semester> overlappingSemester = list(queryWrapper);

        if (!CollectionUtils.isEmpty(overlappingSemester)) {
            return R.Failed(String.format("学期时间和<%s>时间重叠", overlappingSemester.get(0).getName()));
        }

        // 校验学期是否从周一开始
        if (!isValidSemesterDates(semesterInfoDTO.getStartTime().toLocalDate())) {
            return R.Failed("学期只能从周一开始");
        }
        Semester semester = BeanCopyUtil.copy(semesterInfoDTO, Semester.class);
        semester.setUpdater(UserUtil.getCurrentUser().getUsername());
        int row = semesterMapper.insert(semester);
        if (row > 0) {
            return R.Success("学期新增成功！");
        }
        return R.Failed("学期新增失败！");
    }

    /**
     * 根据条件查询学期信息，条件为空查询所有信息
     *
     * @param semesterPageDTO
     * @return
     */
    @Override
    public R<?> getSemesterPageByCondition(SemesterPageDTO semesterPageDTO) {
        // 开启分页
        Page<Semester> page = new Page<>(semesterPageDTO.getPageNo(), semesterPageDTO.getPageSize());
        // 定义查询条件
        LambdaQueryWrapper<Semester> queryWrapper = new LambdaQueryWrapper();
        queryWrapper
                .like(!ObjectUtils.isEmpty(semesterPageDTO.getName()), Semester::getName, semesterPageDTO.getName());
        Page<Semester> semesterPage = semesterMapper.selectPage(page, queryWrapper);
        PageVO<SemesterPageVO> semesterPageVOPageVO = new PageVO<>();
        BeanUtils.copyProperties(semesterPage, semesterPageVOPageVO);
        return R.Success(semesterPageVOPageVO);
    }

    /**
     * 更新学期信息
     *
     * @param semesterInfoDTO
     * @return
     */
    @Override
    public R<?> updateSemester(SemesterInfoDTO semesterInfoDTO) {
        // 校验学期时间间隔不超过7个月
        if (!isWithinSevenMonths(semesterInfoDTO.getStartTime(), semesterInfoDTO.getEndTime())) {
            return R.Failed("学期时间间隔不能超过7个月");
        }

        // 校验名称是否合法
        if (!isValidSemesterName(semesterInfoDTO.getName())) {
            return R.Failed("学期名称格式错误，学期格式只能为：例2023-2024学年第二学期");
        }

        // 校验学期时间是否重叠
        LambdaQueryWrapper<Semester> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.ne(Semester::getId, semesterInfoDTO.getId())
                .ge(Semester::getEndTime, semesterInfoDTO.getStartTime())
                .le(Semester::getStartTime, semesterInfoDTO.getEndTime());
        List<Semester> overlappingSemester = list(queryWrapper);
        if (!CollectionUtils.isEmpty(overlappingSemester)) {
            return R.Failed(String.format("学期时间和<%s>时间重叠", overlappingSemester.get(0).getName()));
        }

        // 校验名称是否存在
        queryWrapper.clear();
        queryWrapper.ne(Semester::getId, semesterInfoDTO.getId())
                .eq(Semester::getName, semesterInfoDTO.getName());
        Semester semester5 = getOne(queryWrapper);
        if (!ObjectUtils.isEmpty(semester5) && !semester5.getId().equals(semesterInfoDTO.getId())) {
            return R.Failed("学期名称重复！该学期已经存在！");
        }

        // 校验学期是否从周一开始
        if (!isValidSemesterDates(semesterInfoDTO.getStartTime().toLocalDate())) {
            return R.Failed("学期只能从周一开始");
        }
        Semester semester1 = BeanCopyUtil.copy(semesterInfoDTO, Semester.class);
        semester1.setUpdater(UserUtil.getCurrentUser().getUsername());
        int row = semesterMapper.updateById(semester1);
        if (row > 0) {
            return R.Success("更新成功！");
        }

        return R.Failed("更新失败！");
    }

    /**
     * 删除学期
     *
     * @param id
     * @return
     */
    @Override
    public R<?> deleteSemesterById(Integer id) {
        Semester semester = semesterMapper.selectById(id);
        if (semester.getStartTime().isBefore(LocalDateTime.now())) {
            return R.Failed("已经开始的学期无法进行删除！");
        }
        // TODO 未添加关联删除校验逻辑
        boolean row = removeById(id);
        if (row) {
            return R.Success("学期信息删除成功");
        }
        return R.Failed("学期信息删除失败");
    }

    /**
     * 获取当前时间所在的学期
     *
     * @return
     */
    @Override
    public Semester getCurrentSemester() {
        LocalDateTime now = LocalDateTime.now();
        LambdaQueryWrapper<Semester> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.le(Semester::getStartTime, now)
                .ge(Semester::getEndTime, now);
        Semester semester = getOne(queryWrapper);
        if (semester != null) {
            return semester;
        }

        // 如果当前时间没有对应的学期，则查找最近的开始时间和结束时间
        queryWrapper.clear(); // 清空查询条件
        queryWrapper.orderByAsc(Semester::getStartTime).last("LIMIT 1");
        List<Semester> semesters = list(queryWrapper);
        if (!CollectionUtils.isEmpty(semesters)) {
            return semesters.get(0);
        }

        queryWrapper.clear(); // 再次清空查询条件
        queryWrapper.orderByDesc(Semester::getEndTime).last("LIMIT 1");
        return list(queryWrapper).get(0);
    }


    /**
     * 校验学期的名称格式
     *
     * @param name
     * @return
     */
    private boolean isValidSemesterName(String name) {
        return name.matches("\\d{4}-\\d{4}学年第[一|二]学期");
    }


    /**
     * 校验学期是否是从周一开始
     *
     * @param startDate
     * @return
     */
    private boolean isValidSemesterDates(LocalDate startDate) {
        // 检查学期时间是否从周一开始
        if (startDate.getDayOfWeek() != DayOfWeek.MONDAY) {
            return false;
        }
        return true;
    }

    /**
     * 获取当前时间对应的周数
     *
     * @return
     */
    @Override
    public Integer getWeekNum() {
        Semester semester = getCurrentSemester();
        if (ObjectUtils.isEmpty(semester)) {
            return null;
        }
        LocalDate initialDate = semester.getStartTime().toLocalDate();
        LocalDate currentDate = LocalDate.now();
        // 将initialDate调整为那一年的第一个周天
        LocalDate firstMondayOfInitialYear = initialDate.with(TemporalAdjusters.nextOrSame(DayOfWeek.SUNDAY));
        //计算两个日期之间的天数茶
        long daysBetween = ChronoUnit.DAYS.between(initialDate, currentDate) + 1;
        //将天数差转换为周数，并向上取整（如果不足一周也算作一周）
        int weekNumber = (int) Math.ceil(daysBetween / 7.0);
        // 如果当前时间在第一个周天之前，则周数为1
        if (currentDate.isBefore(firstMondayOfInitialYear)) {
            weekNumber = 1;
        }
        return weekNumber;
    }

    /**
     * 获取所有学期信息
     *
     * @return
     */
    @Override
    public R<List<SemesterInfoVO>> getAllSemesterInfo() {
        LambdaQueryWrapper<Semester> queryWrapper = new LambdaQueryWrapper<Semester>().orderByDesc(Semester::getEndTime);
        List<Semester> semesterList = list(queryWrapper);
        List<SemesterInfoVO> semesterInfoVOS = new ArrayList<>();
        LocalDateTime now = LocalDateTime.now();
        for (Semester semester : semesterList) {
            SemesterInfoVO semesterInfoVO = BeanCopyUtil.copy(semester, SemesterInfoVO.class);
            if (now.isAfter(semester.getStartTime()) && now.isBefore(semester.getEndTime())) {
                semesterInfoVO.setCurrentFlg(true);
            }
            semesterInfoVOS.add(semesterInfoVO);
        }
        if (now.isAfter(semesterInfoVOS.get(0).getEndTime())) {
            semesterInfoVOS.get(0).setCurrentFlg(true);
        }
        return R.Success(semesterInfoVOS);
    }


    /**
     * 根据id校验学期是否是当前学期或者下一学期
     *
     * @param id
     * @return
     */
    public boolean checkSemesterById(Integer id) {
        Semester semester = getById(id);
        LocalDateTime now = LocalDateTime.now();
        return now.isAfter(semester.getStartTime()) && now.isBefore(semester.getEndTime()) || now.isBefore(semester.getStartTime());
    }

    /**
     * 校验学期间隔是否超过7个月
     *
     * @param startTime
     * @param endTime
     * @return
     */
    public boolean isWithinSevenMonths(LocalDateTime startTime, LocalDateTime endTime) {
        return Math.abs(ChronoUnit.MONTHS.between(startTime.toLocalDate(), endTime.toLocalDate())) <= 7;
    }

}
